<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app"></div>
    <script>
      // 定义一个副作用函数桶
      const bucket = new Map();

      // 定义一个全局变量，保存当前正在执行的副作用函数
      let activeEffect = null;

      function isObject(value) {
        return typeof value === "object" && value !== null;
      }

      function reactive(data) {
        if (!isObject) return;

        return new Proxy(data, {
          get(target, key) {
            if (activeEffect) {
              let depSet
              // 如果桶内没有存这个属性名
              if(!bucket.get(key)) {
                depSet = new Set()
                bucket.set(key, depSet)
              }

              // 只有activeEffect有值时，才添加到桶中
              // depSet 依旧是Set结构，同样先天就可以去重
              depSet.add(activeEffect);
            }
            return target[key];
          },
          set(target, key, value) {
            target[key] = value;
            // 调用副作用函数
            let depSet = bucket.get(key)
            if(depSet) {
              depSet.forEach((fn) => fn())
            }
            return true;
          },
        });
      }
      /**
       * 注册副作用函数
       * @param {*} fn
       */
      function registEffect(fn) {
        if (typeof fn !== "function") return;
        // 记录正在执行的副作用函数
        activeEffect = fn;
        // 调用
        fn();
        // 重置全局变量
        activeEffect = null;
      }
      const state = { name: "孙悟空", age: 18 };
      // 定义一个变量，触发者
      const proxy = reactive(state);

      // 定义一个函数，响应者
      function effectName() {
        console.log("effectName被执行了...");
        app.innerHTML = proxy.name;
      }
      registEffect(effectName);

      function effectAge() {
        console.log("effectAge被执行了...", proxy.age);
      }
      registEffect(effectAge);

      // 模拟数据的改变
      setTimeout(() => {
        proxy.name = "猪八戒";
      }, 1000);
      console.log("bucket", bucket);
    </script>
  </body>
</html>
